/*********************************************************************
* File: Combat.cpp
*
* \brief Implements the combat API.
*
* \author Instituto Superior Tecnico de Lisboa, Portugal
* \author Technical University of Lisbon, Portugal
* 
* \author Agentes Autonomos e Sistemas Multiagente
* \author Autonomous Agents and Multiagent Systems
* 
* \version	1.0
* \date		2006/2007
*********************************************************************/
#include "Combat.h"
#include "../../util/util.h"
#include "../../util/debug.h"
#include "../../util/mathLibrary.h"
#include "../Agent.h"
using namespace agents;

const float ICombat::sufficientDistanceToDefuseBomb = 100.0f;

/**
* \brief The class constructor.
*
* \param[in] The agent.
**/
Combat::Combat(Agent &owner)
:_owner(owner)
{
	setCurrentWeapon(WeaponIds::null);
	memset(&(ammoInClip), 0, sizeof(ammoInClip));
	memset(&(ammo), 0, sizeof(ammo));
}

/**
* \brief The class destructor.
**/
Combat::~Combat(){}

/**
* \brief Setups for the new round. 
* 
* Should be invoked when a new round starts.
**/
void Combat::newRound()
{
	_isInBombSite = false;
}
/**
* \brief Should be invoked once at end of the cycle. 
**/
void Combat::endCycle()
{
	_isInBombSite = false;
}

/**
* \brief Whether the agent has a certain weapon.
*
* \param[in] name The weapon name.
*
* \see agents::interfaces::ICombat::hasWeapon(char*)
**/
bool Combat::hasWeapon(char* name)
{
   return hasWeapon(weaponNameToId(name));
}
/**
* \brief Whether the agent has a certain weapon.
*
* \param[in] name The weapon id.
*
* \see agents::interfaces::ICombat::hasWeapon(WeaponIds::Enumeration)
**/
bool Combat::hasWeapon(WeaponIds::Enumeration id)
{
	if((id == WeaponIds::ammoPrimary) || (id == WeaponIds::ammoSecondary) 
		|| (id == WeaponIds::vest) || (id == WeaponIds::vestHelm)
		|| (id == WeaponIds::nightVisionGoggles))
		return false; // This method should not be used to find out about these items

	if(id == WeaponIds::shieldGun)
		return (strncmp(STRING(_owner.pev->viewmodel), "models/shield/v_shield_", 23) == 0);
	if(id == WeaponIds::defuser)
		return hasDefuser;

	int weapons = _owner.pev->weapons;  
	return (weapons & (1 << id)) != 0;
}

/**
* \brief Whether the agent has any primary weapon.
*
* \see agents::interfaces::ICombat::hasPrimaryWeapon
**/
bool Combat::hasPrimaryWeapon() 
{
   return (_owner.pev->weapons & WEAPON_PRIMARY) != 0;
}
/**
* \brief Whether the agent has any secondary weapon.
*
* \see agents::interfaces::ICombat::hasSecondaryWeapon
**/
bool Combat::hasSecondaryWeapon() 
{
  return (_owner.pev->weapons & WEAPON_SECONDARY) != 0;
}
/**
 * \brief Whether the agent is carrying the bomb.
*
* \see agents::interfaces::ICombat::hasBomb
 **/
bool Combat::hasBomb()
{
	return hasWeapon(WeaponIds::c4);
}
/**
 * \brief Gets the primary weapon id.
*
* \see agents::interfaces::ICombat::getPrimaryWeaponId
 **/
WeaponIds::Enumeration Combat::getPrimaryWeaponId() 
{
	int mask = _owner.pev->weapons;

	if(mask & 1<<WeaponIds::m3)				{ return WeaponIds::m3;      }
	else if(mask & 1<<WeaponIds::xm1014)	{ return WeaponIds::xm1014;  }
	else if(mask & 1<<WeaponIds::mac10)		{ return WeaponIds::mac10;   }
	else if(mask & 1<<WeaponIds::tmp)		{ return WeaponIds::tmp;     }
	else if(mask & 1<<WeaponIds::mp5Navy)	{ return WeaponIds::mp5Navy; }
	else if(mask & 1<<WeaponIds::ump45)		{ return WeaponIds::ump45;   }
	else if(mask & 1<<WeaponIds::p90)		{ return WeaponIds::p90;     }
	else if(mask & 1<< WeaponIds::famas)	{ return WeaponIds::famas;   }
	else if(mask & 1<<WeaponIds::galil)		{ return WeaponIds::galil;   }
	else if(mask & 1<<WeaponIds::ak47)		{ return WeaponIds::ak47;    }
	else if(mask & 1<<WeaponIds::scout)		{ return WeaponIds::scout;   }
	else if(mask & 1<<WeaponIds::m4A1)		{ return WeaponIds::m4A1;    }
	else if(mask & 1<<WeaponIds::aug)		{ return WeaponIds::aug;     }
	else if(mask & 1<<WeaponIds::sg550)		{ return WeaponIds::sg550;   }
	else if(mask & 1<<WeaponIds::sg552)		{ return WeaponIds::sg552;   }
	else if(mask & 1<<WeaponIds::awp)		{ return WeaponIds::awp;     }
	else if(mask & 1<<WeaponIds::g3Sg1)		{ return WeaponIds::g3Sg1;   }
	else if(mask & 1<<WeaponIds::m249)		{ return WeaponIds::m249;    }
	return WeaponIds::null;
}

/**
 * \brief Gets the secondary weapon id.
*
* \see agents::interfaces::ICombat::getSecondaryWeaponId
 **/
WeaponIds::Enumeration Combat::getSecondaryWeaponId() 
{
  int mask = _owner.pev->weapons;
	if(mask & 1<<WeaponIds::glock18)		{ return WeaponIds::glock18;   }
	else if(mask & 1<<WeaponIds::usp)       { return WeaponIds::usp;       }
	else if(mask & 1<<WeaponIds::p228)      { return WeaponIds::p228;      }
	else if(mask & 1<<WeaponIds::dEagle)    { return WeaponIds::dEagle;    }
	else if(mask & 1<<WeaponIds::elite)     { return WeaponIds::elite;     }
	else if(mask & 1<<WeaponIds::fiveSeven) {	return WeaponIds::fiveSeven; }
	return WeaponIds::null;
}



/**
* \brief Gets the agent's armour value.
*
* \see agents::interfaces::ICombat::armour
**/
int Combat::armour() {
  return _owner.pev->armorvalue;
}
/**
* \brief Get the ammunition count.
*
* \see agents::interfaces::ICombat::getAmmo
**/
int Combat::getAmmo(void)
{ 
	if (g_weaponDefinitions[_currentWeapon].ammo == -1) 
		return 0; 
	else return ammo[g_weaponDefinitions[_currentWeapon].ammo]; 
}
/**
* \brief Get the ammunition in clip.
*
* \see agents::interfaces::ICombat::getAmmoInClip
**/
int Combat::getAmmoInClip(void)
{ 
	return ammoInClip[_currentWeapon]; 
}
/**
* \brief Primary attack (left mouse button).
*
* \see agents::interfaces::ICombat::primaryAttack
**/
void Combat::primaryAttack() {
  _owner.pev->button |= IN_ATTACK;
}
/**
 * \brief Secondary attack (right mouse button).
*
* \see agents::interfaces::ICombat::secondaryAttack
 **/
void Combat::secondaryAttack() {
  _owner.pev->button |= IN_ATTACK2;
}
/**
 * \brief Reload weapon.
*
* \see agents::interfaces::ICombat::reloadWeapon
 **/
void Combat::reloadWeapon() 
{
	 if(!(_owner.pev->oldbuttons & IN_RELOAD))
		 _owner.pev->button |= IN_RELOAD;
 }

/**
 * \brief Plant bomb. 
*
* \see agents::interfaces::ICombat::plantBomb
 **/
bool Combat::plantBomb()
{
	if (_owner.pev->weapons & (1<<WeaponIds::c4))
	{
		selectWeapon("weapon_c4");

		_owner.pev->button |= (IN_ATTACK | IN_DUCK);
		return true;
	}
	return false;
}
/**
 * \brief Defuses the bomb. 
*
* \see agents::interfaces::ICombat::defuseBomb
 **/
void Combat::defuseBomb()
{
  _owner.pev->button |= (IN_USE | IN_DUCK);
}

/**
* \brief Select weapon by name.
*
* \param[in] name The weapon name.
*
* \see agents::interfaces::ICombat::selectWeapon
**/
void Combat::selectWeapon(char* name)
{
   util::fakeClientCommand(_owner.getEdict(), name);
}

/**
* \brief Select weapon by id.
*
* \param[in] id The weapon id.
*
* \see agents::interfaces::ICombat::selectWeapon
**/
void Combat::selectWeapon(WeaponIds::Enumeration id)
{
   util::fakeClientCommand(_owner.getEdict(), g_weapons[weaponIdToIndex(id)].name);
}


/**
* \brief Select the primary weapon.
*
* \see agents::interfaces::ICombat::selectPrimaryWeapon
**/
void Combat::selectPrimaryWeapon()
{
  selectWeapon(getPrimaryWeaponId());
}

/**
* \brief Select the secondary weapon.
*
* \see agents::interfaces::ICombat::selectSecondaryWeapon
**/
void Combat::selectSecondaryWeapon()
{
  selectWeapon(getSecondaryWeaponId());
}

/**
* \brief Drops the current weapon.
*
* \see agents::interfaces::ICombat::drop
**/
void Combat::drop()
{
	util::fakeClientCommand(_owner.getEdict(), "drop");
}
/** 
* \brief Gets the gun's position.
*
* \see agents::interfaces::ICombat::getGunPosition
**/
Vector Combat::getGunPosition() 
{ 
	return (_owner.pev->origin + _owner.pev->view_ofs); 
}
/**
* \brief Gets the agent's aiming angles.
*
* \see agents::interfaces::ICombat::getAngles
**/
Vector Combat::getAngles()
{
	return _owner.pev->angles;
}
/**
* \brief Sets the agent's aiming angles.
**/
void Combat::setAngles(Vector angles)
{
	//BUG 02: Incorrect aiming pitch : Start
	_owner.pev->angles = angles;
	angles.x *= -1.0;
	_owner.pev->v_angle = angles;
	//BUG 02: End
}
/**
* \brief Rotates the agent left.
*
* \see agents::interfaces::ICombat::aimLeft
**/
void Combat::aimLeft()
{
	Vector angles = getAngles();
	angles.y += aimIncrement();
	setAngles(angles);
}
/**
* \brief Rotates the agent right.
*
* \see agents::interfaces::ICombat::aimRight
**/
void Combat::aimRight()
{
	Vector angles = getAngles();
	angles.y -= aimIncrement();
	setAngles(angles);
}
/**
* \brief Rotates the agent up.
*
* \see agents::interfaces::ICombat::aimUp
**/
void Combat::aimUp()
{
	Vector angles = getAngles();
	angles.x += aimIncrement();
	setAngles(angles);
}
/**
* \brief Rotates the agent down.
*
* \see agents::interfaces::ICombat::aimDown
**/
void Combat::aimDown()
{
	Vector angles = getAngles();
	angles.x -= aimIncrement();
	setAngles(angles);
}
/** 
* \brief Aim increment for this frame.
**/
float Combat::aimIncrement()
{
	return gpGlobals->frametime * MAXIMUM_MOTION_ROTATION_SPEED;
}
/**
* \brief Adjust aiming angles according to angle deviation.
*
* \param[in] deviation The intended angle deviation.
**/
void Combat::aim(Vector deviation)
{
	util::math::clampAngles(deviation);

	Vector angles = getAngles();
	float xIncrement = min(fabs(deviation.x), aimIncrement());
	if(deviation.x > 0)
		angles.x += xIncrement;
	else
		angles.x -= xIncrement;

	float yIncrement = min(fabs(deviation.y), aimIncrement());
	if(deviation.y > 0)
		angles.y += yIncrement;
	else
		angles.y -= yIncrement;

	setAngles(angles);
}
/**
* \brief Aim towards a target instantaneously.
*
* \param[in] target The target.
**/
void Combat::aimAtInstantaneously(Vector target)
{	
	Vector viewAngles = util::math::vectorToAngles(target - getGunPosition());
	util::math::clampAngles(viewAngles);

	setAngles(viewAngles);
}
/**
* \brief Aim at a target.
*
* Aiming is not instantaneous and, therefore, this actuator should be 
* invoked througout several cycles.
*
* \param[in] target The target.
*
* \see agents::interfaces::ICombat::aimAt
**/
void Combat::aimAt(Vector target)
{
	Vector viewAngles = util::math::vectorToAngles(target - getGunPosition());
	util::math::clampAngles(viewAngles);
	Vector deviation = viewAngles - getAngles();

	aim(deviation);
}
/**
* \brief Whether the agent is aiming in the direction of a target.
*
* \param[in] target The target.
*
* \see agents::interfaces::ICombat::isAimingAt
**/
bool Combat::isAimingAt(Vector target)
{
	Vector viewAngles = util::math::vectorToAngles(target - getGunPosition());
	util::math::clampAngles(viewAngles);
	Vector deviation = viewAngles - getAngles();
	Vector vZero(0,0,0);
	return util::math::compareVectors(deviation, vZero, 0);
}
/**
* \brief Gets information about a weapon.
*
* \param[in] id The weapon id.
* \return The weapon info.
*
* \see agents::interfaces::ICombat::getWeaponInfo
**/
WeaponInfo* Combat::getWeaponInfo(WeaponIds::Enumeration id)
{
	for(int i = 0; g_weapons[i].id!=WeaponIds::null; i++)
		if (g_weapons[i].id == id)
			return &g_weapons[i];
	return NULL;
}
/**
* \brief Weapon id to index in the 'g_weapons' array
*
* \param[in] name The weapon id.
**/
int Combat::weaponIdToIndex(WeaponIds::Enumeration id)
{
	for(int i = 0; g_weapons[i].id!=WeaponIds::null; i++)
		if (g_weapons[i].id == id)
			return i;
	return -1;
}


/**
* \brief Weapon name to index in the 'g_weapons' array.
*
* \param[in] name The weapon name.
**/
int Combat::weaponNameToIndex(char* name)
{
	return weaponIdToIndex(weaponNameToId(name));
}
/**
* \brief Weapon name to id.
*
* \param[in] name The weapon name.
**/
WeaponIds::Enumeration Combat::weaponNameToId(char* name)
{
	WeaponInfo *pSelect = g_weapons;

	for(int i=0; pSelect[i].id!=WeaponIds::null; i++)
	{
		if(strcmp(pSelect[i].name, name) == 0)
			return (WeaponIds::Enumeration) pSelect[i].id;
	}

	return WeaponIds::null;
}
/**
* \brief Weapon model name to id.
*
* \param[in] name The weapon model name.
**/
WeaponIds::Enumeration Combat::weaponModelNameToId(const char* name)
{
	WeaponInfo *pSelect = g_weapons;

	for(int i=0; pSelect[i].id!=WeaponIds::null; i++)
	{
		if(strcmp(pSelect[i].model, name) == 0)
			return (WeaponIds::Enumeration) pSelect[i].id;
	}

	return WeaponIds::null;
}
